/*
 * CircuitTest.h
 *
 * Created on : 24-05-2014
 *     Author : A.W. Janisse
 *
 * Version 1.0: 24-05-2014, Final release
 */

#include "gtest/gtest.h"

#include "Circuit.h"
#include "Low.h"

using namespace std;

/**
 * Deze test toon aan dat er een circuit object aangemaakt en opgeruimd (delete) kan
 * worden.
 *
 * De test case is geslaag als de instantie van circuit niet NULL is en er geen runtime
 * error komen.
 */
TEST(Circuit, ConstructorDestructorTest)
{
    Circuit *circuit = new Circuit();
    ASSERT_TRUE(circuit != NULL);
    delete circuit;
}

/**
 * Deze test toont aan dat er een 'Not' gate toegevoegd kan worden.
 *
 * De test is geslaagd als addGate een true terug geeft.
 */
TEST(Circuit, AddNotReturnTrue)
{
    Circuit circuit;
    EXPECT_EQ(true, circuit.addGate(0,0, "Not"));
}

/**
 * Deze test toont aan dat er een 'Low' gate toegevoegd kan worden.
 *
 * De test is geslaagd als addGate een true terug geeft.
 */
TEST(Circuit, AddLowReturnTrue)
{
    Circuit circuit;
    EXPECT_EQ(true, circuit.addGate(0,0, "Low"));
}

/**
 * Deze test toont aan dat er een 'High' gate toegevoegd kan worden.
 *
 * De test is geslaagd als addGate een true terug geeft.
 */
TEST(Circuit, AddHighReturnTrue)
{
    Circuit circuit;
    EXPECT_EQ(true, circuit.addGate(0,0, "High"));
}

/**
 * Deze test toont aan dat er een 'Or' gate toegevoegd kan worden.
 *
 * De test is geslaagd als addGate een true terug geeft.
 */
TEST(Circuit, AddOrReturnTrue)
{
    Circuit circuit;
    EXPECT_EQ(true, circuit.addGate(0,0, "Or"));
}

/**
 * Deze test toont aan dat er een 'And' gate toegevoegd kan worden.
 *
 * De test is geslaagd als addGate een true terug geeft.
 */
TEST(Circuit, AddAndReturnTrue)
{
    Circuit circuit;
    EXPECT_EQ(true, circuit.addGate(0,0, "And"));
}

/**
 * Deze test toont aan dat onbekende gate niet toegevoegd kan worden.
 *
 * De test is geslaagd als addGate een false terug geeft.
 */
TEST(Circuit, AddWrongTypeReturnFalse)
{
    Circuit circuit;
    EXPECT_EQ(false, circuit.addGate(0,0, "ttt"));
}

/**
 * Deze test toont aan dat meerdere gates toegevoegd kunnen worden.
 *
 * De test is geslaagd als voor iedere addGate een true terug komt.
 */
TEST(Circuit, AddMultipleReturnTrue)
{
    Circuit circuit;
    EXPECT_EQ(true, circuit.addGate(0,0,   "Not" ));
    EXPECT_EQ(true, circuit.addGate(10,10, "Low" ));
    EXPECT_EQ(true, circuit.addGate(20,20, "High"));
    EXPECT_EQ(true, circuit.addGate(30,30, "Or"  ));
    EXPECT_EQ(true, circuit.addGate(40,40, "And" ));
}

/**
 * Deze test toont aan twee gates op de zelde x maar op een verschillende y toegevoegd kunnen
 * worden.
 *
 * De test is geslaagd als addGate true terug geeft.
 */
TEST(Circuit, AddTwoOnSameXReturnTrue)
{
    Circuit circuit;
    circuit.addGate(0,0, "Not" );
    EXPECT_EQ(true, circuit.addGate(0,50, "Low" ));
}

/**
 * Deze test toont aan twee gates op de zelde Y maar op een verschillende X toegevoegd kunnen
 * worden.
 *
 * De test is geslaagd als addGate true terug geeft.
 */
TEST(Circuit, AddTwoOnSameYReturnTrue)
{
    Circuit circuit;
    EXPECT_EQ(true, circuit.addGate(0,0,  "Not" ));
    EXPECT_EQ(true, circuit.addGate(10,0, "Low" ));
}

/**
 * Deze test toont aan twee gates op de zelde X en Y niet toegevoegd kunnen
 * worden.
 *
 * De test is geslaagd als addGate flase terug geeft.
 */
TEST(Circuit, AddTwoOnSameXYReturnFalse)
{
    Circuit circuit;
    circuit.addGate(10,10,  "Not" );
    EXPECT_EQ(false, circuit.addGate(10,10, "Low" ));
}

/**
 * Deze test toont aan het toevoegen kan slagen als de X posities voldoende verschillen.
 *
 * De test is geslaagd als addGate flase terug geeft.
 */
TEST(Circuit, AddTwoOnGoodDeltaXReturnTrue)
{
    Circuit circuit;
    circuit.addGate(0,0, "Not" );
    EXPECT_EQ(true, circuit.addGate(8,0, "Low" ));
}

/**
 * Deze test toont aan het toevoegen kan slagen als de Y posities voldoende verschillen.
 *
 * De test is geslaagd als addGate flase terug geeft.
 */
TEST(Circuit, AddTwoOnGoodDeltaYReturnTrue)
{
    Circuit circuit;
    circuit.addGate(0,0, "Not" );
    EXPECT_EQ(true, circuit.addGate(0,6, "Low" ));
}

/**
 * Deze test toont aan dat het wissen van een gate op een foute X,Y positie niet kan.
 *
 * De test is geslaagd als removeGate false terug geeft.
 */
TEST(Circuit, RemoveUnknownGateReturnFalse)
{
    Circuit circuit;
    circuit.addGate(0,0, "Not" );
    EXPECT_EQ(false, circuit.removeGate(10,10));
}

/**
 * Deze test toont aan dat het wissen van een gate op juiste foute X,Y positie wel kan.
 *
 * De test is geslaagd als removeGate true terug geeft.
 */
TEST(Circuit, RemoveGateReturnTrue)
{
    Circuit circuit;
    circuit.addGate(0,0, "Not" );
    EXPECT_EQ(true, circuit.removeGate(0,0));
}

/**
 * Deze test toont aan dat als een gate wordt verwijderd die verbonden is met een andere gate, dat
 * bij de andere gate deze entry referentie wordt verwijderd.
 */
TEST(Circuit, RemoveConnectedGateWorksOk)
{
    Circuit circuit;

    EXPECT_EQ(true, circuit.addGate(0,0, "High"));
    EXPECT_EQ(true, circuit.addGate(10,0, "Not"));
    EXPECT_EQ(true, circuit.addGate(20,0, "Not"));

    EXPECT_EQ(true, circuit.addConnection(0,0,10,0));
    EXPECT_EQ(true, circuit.addConnection(10,0,20,0));

    EXPECT_EQ(true, circuit.removeGate(0,0));
}

/**
 * Deze test voeg twee gates toe. Deze gates worden verbonden met een OR gate.
 *
 * De test is geslaagd als:
 *  1. De eerste gate probleemloos verwijderd kan worden.
 *  2. De tweede gate probleemloos verwijderd kan worden.
 */
TEST(Circuit, RemoveSecondGateWorksOk)
{
    Circuit circuit;

    EXPECT_EQ(true, circuit.addGate(0,0, "High"));
    EXPECT_EQ(true, circuit.addGate(0,20, "Low"));
    EXPECT_EQ(true, circuit.addGate(20,10, "Or"));

    EXPECT_EQ(true, circuit.addConnection(0, 0,20,10));
    EXPECT_EQ(true, circuit.addConnection(0,20,20,10));

    EXPECT_EQ(true, circuit.removeGate(0, 0));
    EXPECT_EQ(true, circuit.removeGate(0,20));
}

/**
 * Deze testcase probeert een connectie toe te voegen terwijl er nog geen gates
 * bestaan.
 *
 * De testcase is geslaagd als addConnection false terug geeft.
 */
TEST(Circuit, AddConnectionNoGatesAddedReturnFalse)
{
    Circuit circuit;
    EXPECT_EQ(false, circuit.addConnection(0,0,0,0));
}

/**
 * Deze testcase toont aan dat er geen connectie gemaakt kan worden op een gate met de
 * zelfde X en Y.
 *
 * De test is geslaag als addConnection false terug geeft.
 */
TEST(Circuit, AddConnectionOnSameGateReturnFalse)
{
    Circuit circuit;
    circuit.addGate(10,10, "And" );
    EXPECT_EQ(false, circuit.addConnection(12,12,12,12));
}

/**
 * Deze testcase toont aan dat er een connectie gemaakt kan worden als er twee gates bestaan
 * en er zijn voldoende in- en outputs vrij.
 *
 * De test is geslaagd als addConnection true terug geeft.
 */
TEST(Circuit, AddConnectionReturnTrue)
{
    Circuit circuit;
    circuit.addGate(10,10, "And");
    circuit.addGate(100,10, "Or");
    EXPECT_EQ(true, circuit.addConnection(10,10,100,10));
}

/**
 * Deze testcase toont aan dat als x1 < x2 en er is geen vrije ingang voor gate(x2,y2) of
 * geen vrije uitgang gate(x1,y2) is, dat er dan geen connectie mogelijk is.
 *
 * De testcase is geslaagd als:
 *  1. addConnection met x1 < x2 en geen vrije ingang voor gate(x2,y2) geeft false.
 *  2. addConnection met x1 < x2 en geen vrije uitgang voor gate(x1,y1) geeft false.
 */
TEST(Circuit, AddConnectioAndx1IsSmallerThanx2ReturnFalse)
{
    Circuit circuit;

    EXPECT_EQ(true, circuit.addGate(10,10, "High"));
    EXPECT_EQ(true, circuit.addGate(100,10, "Not"));
    EXPECT_EQ(true, circuit.addGate(20,20, "High"));

    EXPECT_EQ(true, circuit.addConnection(10,10,100,10));

    EXPECT_EQ(false, circuit.addConnection(10,10,20,10)); // Geen vrije ingang
    EXPECT_EQ(false, circuit.addConnection(10,10,20,20)); // Geen vrije uitgang
}

/**
 * Deze testcase toont aan dat als x2 < x1 en er is geen vrije ingang voor gate(x1, y1) of
 * geen vrije uitgang gate(x2,y2) is, dat er dan geen connectie gemaakt kan worden.
 *
 * De testcase is geslaagd als:
 *  1. addConnection met x2 < x1 en geen vrije ingang voor gate(x1,y1) geeft false.
 *  2. addConnection met x2 < x1 en geen vrije uitgang voor gate(x2,y2) geeft false.
 */
TEST(Circuit, AddConnectioAndx2IsSmallerThanx1ReturnFalse)
{
    Circuit circuit;

    circuit.addGate(10,10, "High");
    circuit.addGate(20,10, "Not");
    circuit.addGate(20,20, "High");

    EXPECT_EQ(true, circuit.addConnection(10,10,20,10));

    EXPECT_EQ(false, circuit.addConnection(20,10,10,10)); // Geen vrije ingang
    EXPECT_EQ(false, circuit.addConnection(20,20,10,10)); // Geen vrije uitgang
}

/**
 * Deze test toont aan dat een niet connectie ook niet gewist kan worden.
 *
 * De test is geslaagd als removeConnection false terug geeft.
 */
 TEST(Circuit, RemoveConnectionReturnsFalse)
 {
     Circuit circuit;
     circuit.addGate(10,10, "High");
     circuit.addGate(100,10, "Not");
     circuit.addConnection(10,10,100,10);

     EXPECT_EQ(false, circuit.removeConnection(60,60,80,80)); // Remove on different gates
 }

/**
 * Deze testcase toon aan dat alleen bestaande connectie gewist kunnen
 *
 * De test is geslaagd als removeConnection true terug geeft.
 */
 TEST(Circuit, RemoveConnectionReturnsTrue)
 {
    Circuit circuit;

    circuit.addGate(10,10, "High");
    circuit.addGate(100,10, "Not");
    circuit.addConnection(10,10,100,10);

    EXPECT_EQ(true, circuit.removeConnection(10,10,100,10));
 }

/**
 * Deze test toon aan dat het serialiseren juist werk. Hiervoor worden een aantal gates en
 * een connectie toegevoed. Dit wordt geserialiseerd onclusief scheidingstekens (\n).
 *
 * De test is geslaagd als de geserialiseerde string correct is.
 */
TEST(Circuit, SerializeWorksOK)
{
    Circuit circuit;
    circuit.addGate(10,10, "High");
    circuit.addGate(100,10, "Not");
    circuit.addConnection(10,10,100,10);

    EXPECT_EQ("High,10,10\nNot,100,10\nConnection,10,10,100,10\n", circuit.serialize());
}

/**
 * Deze test toont aan dat het deserialiseren werkt. Dit wordt gedaan door de methode
 * aan te roepen. In de aanroep worden twee gates en een connectie gemaakt. De worden
 * later weer verwijderd.
 *
 * De test is geslaagd als de toegevoede gates en de connectie verwijderd kunnen worden.
 */
TEST(Circuit, DeserializeWorksOK)
{
    Circuit circuit;
    circuit.deserialize("And,0,0\nOr,10,0\nconnection,0,0,10,0\n\n");

    EXPECT_EQ(true, circuit.removeConnection(0,0,10,0));
    EXPECT_EQ(true, circuit.removeGate(0,0));
    EXPECT_EQ(true, circuit.removeGate(10,0));
}

/**
 * Deze test toont aan dat clearCircuit juist werkt. Nadat er een aantal gates zijn toegevoegd
 * zal de serialize string worden opgevraagd.
 *
 * De test is geslaagd als de opgevraagde string leeg is.
 */
TEST(Circuit, clearCircuitWorksOK)
{
    Circuit circuit;
    circuit.addGate(10,10, "High");
    circuit.addGate(100,10, "Not");
    circuit.addConnection(10,10,100,10);

    circuit.clearCircuit();

    EXPECT_EQ("", circuit.serialize());
}





